/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#ifndef MOE_ASSIGN_POINTER_FUNC_HPP
#define MOE_ASSIGN_POINTER_FUNC_HPP

#include <boost/static_assert.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include "AssignProxy.hpp"
#include "../ObjectTypeManager.h"
#include "../RawByteVector.h"

namespace MOE
{

namespace Assign
{

namespace Functors
{

template <class T, class RetType>
struct PointerFuncGet
{
	PointerFuncGet(RetType (T::* func)() const)
		: _memFunction(func)
	{
	}

	void operator() (void* object, void* pData)
	{
		*static_cast<FieldType**>(pData) = (static_cast<T*>(object)->*_memFunction)();
	}

private:
	typedef RetType (T::* FuncType)() const;
	typedef typename boost::remove_pointer<RetType>::type RPFieldType;
	typedef typename boost::remove_cv<RPFieldType>::type FieldType;
	FuncType _memFunction;
};

template <class T, class ParamType, class RetType>
struct PointerFuncSet
{
	PointerFuncSet(RetType (T::* func)(ParamType))
		: _memFunction(func)
	{
	}

	void operator() (void* object, const void* pData)
	{
		(static_cast<T*>(object)->*_memFunction)(*static_cast<FieldType* const*>(pData));
	}

private:
	typedef RetType (T::* FuncType)(ParamType);
	typedef typename boost::remove_pointer<ParamType>::type RPFieldType;
	typedef typename boost::remove_cv<RPFieldType>::type FieldType;
	FuncType _memFunction;
};

}

template <class T, class RetType>
AssignProxy PointerGetter(const RawByteVector& name, RetType (T::* func)() const)
{
	typedef typename boost::remove_pointer<RetType>::type RPFieldType;
	typedef typename boost::remove_cv<RPFieldType>::type FieldType;
	return AssignProxy(
		name,
		DataMemberMeta(ObjectTypeManager::GetClassID<FieldType>(), DT_Pointer)
			.DefineGetter(Functors::PointerFuncGet<T, RetType>(func))
		);
}

template <class T, class ParamType, class RetType>
AssignProxy PointerSetter(const RawByteVector& name, RetType (T::* func)(ParamType))
{
	typedef typename boost::remove_pointer<ParamType>::type RPFieldType;
	typedef typename boost::remove_cv<RPFieldType>::type FieldType;
	return AssignProxy(
		name,
		DataMemberMeta(ObjectTypeManager::GetClassID<FieldType>(), DT_Pointer)
			.DefineSetter(Functors::PointerFuncSet<T, ParamType, RetType>(func))
		);
}

template <class T, class GetterReturn, class SetterParam, class SetterReturn>
AssignProxy PointerAccessors(const RawByteVector& name, GetterReturn (T::* getter)() const, SetterReturn (T::* setter)(SetterParam))
{
	typedef typename boost::remove_pointer<GetterReturn>::type RPGetterFieldType;
	typedef typename boost::remove_cv<RPGetterFieldType>::type GetterFieldType;
	typedef typename boost::remove_pointer<SetterParam>::type RPSetterFieldType;
	typedef typename boost::remove_cv<RPSetterFieldType>::type SetterFieldType;
	typedef typename boost::is_same<GetterFieldType, SetterFieldType> IsSameType;
	BOOST_STATIC_ASSERT(IsSameType::value == true);

	return AssignProxy(
		name,
		DataMemberMeta(ObjectTypeManager::GetClassID<GetterFieldType>(), DT_Pointer)
			.DefineGetter(Functors::PointerFuncGet<T, GetterReturn>(getter))
			.DefineSetter(Functors::PointerFuncSet<T, SetterParam, SetterReturn>(setter))
		);
}

}

}

#endif
